package ui;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.opengl.GLData;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.wb.swt.SWTResourceManager;

import swing2swt.layout.BorderLayout;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.GLContext;

public final class KeplerApp
{
	private static volatile boolean	isActive	= false;
	//========================KEY==========================
	private static boolean			m_key_up	= false;
	private static boolean			m_key_down	= false;
	private static boolean			m_key_left	= false;
	private static boolean			m_key_right	= false;
	//========================UI===========================
	private static Shell			shell;
	private static GLCanvas			canvas;
	//========================FPS==========================
	/** time at last frame */
	private static long				m_lastFrame;
	/** frames per second */
	private static int				m_currentFPS;
	/** last fps time */
	private static long				m_lastFPS;
	/** Timer for sync() */
	private static long				timeThen;
	private static long				timeLate;
	private static Listener			m_canvasListener;
	private static Text				text_0;
	private static Text				text_1;
	private static Text				text_2;
	private static Text				text_3;
	private static Text				text_4;

	/**
	 * Launch the application.
	 * 
	 * @param args
	 */
	public static void main(String[] args)
	{
		try
		{
			KeplerApp window = new KeplerApp();
			window.open();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public static Shell getShell()
	{
		return shell;
	}

	/**
	 * Open the window.
	 */
	public void open()
	{
		final Display display = Display.getDefault();
		createContents(display);

		canvas.setCurrent();
		try
		{
			GLContext.useContext(canvas);

			Composite composite = new Composite(shell, SWT.NONE);
			composite.setLayout(new BorderLayout(0, 0));
			GridData gd_composite = new GridData(SWT.RIGHT, SWT.FILL, false, false, 1, 1);
			gd_composite.widthHint = 159;
			composite.setLayoutData(gd_composite);

			Composite composite_1 = new Composite(composite, SWT.NONE);
			composite_1.setLayoutData(BorderLayout.SOUTH);
			composite_1.setLayout(new GridLayout(1, false));

			final Button startButton = new Button(composite_1, SWT.NONE);
			startButton.setFont(SWTResourceManager.getFont("Tahoma", 8, SWT.BOLD));
			startButton.setSelection(true);
			final Button stopButton = new Button(composite_1, SWT.NONE);
			stopButton.setFont(SWTResourceManager.getFont("Tahoma", 8, SWT.BOLD));
			stopButton.setEnabled(false);
			startButton.addMouseListener(new MouseAdapter()
			{
				@Override
				public void mouseDown(MouseEvent e)
				{
					isActive = true;
					startButton.setEnabled(!startButton.getEnabled());
					stopButton.setEnabled(!stopButton.getEnabled());
					text_0.setEnabled(!isActive);
					text_1.setEnabled(!isActive);
					text_2.setEnabled(!isActive);
					text_3.setEnabled(!isActive);
					text_4.setEnabled(!isActive);

					SolarSystem.m_x = Double.parseDouble(text_0.getText());
					SolarSystem.m_y = Double.parseDouble(text_1.getText());
					SolarSystem.m_vx = Double.parseDouble(text_2.getText());
					SolarSystem.m_vy = Double.parseDouble(text_3.getText());
					SolarSystem.m_st = Double.parseDouble(text_4.getText());
					SolarSystem.init();

				}
			});
			GridData gd_startButton = new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1);
			gd_startButton.widthHint = 149;
			startButton.setLayoutData(gd_startButton);
			startButton.setText("START");

			stopButton.addMouseListener(new MouseAdapter()
			{
				@Override
				public void mouseDown(MouseEvent e)
				{
					isActive = false;
					startButton.setEnabled(!startButton.getEnabled());
					stopButton.setEnabled(!stopButton.getEnabled());
					text_0.setEnabled(!isActive);
					text_1.setEnabled(!isActive);
					text_2.setEnabled(!isActive);
					text_3.setEnabled(!isActive);
					text_4.setEnabled(!isActive);
				}
			});
			GridData gd_stopButton = new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1);
			gd_stopButton.widthHint = 150;
			stopButton.setLayoutData(gd_stopButton);
			stopButton.setText("STOP");

			Composite composite_2 = new Composite(composite, SWT.NONE);
			composite_2.setLayoutData(BorderLayout.CENTER);
			composite_2.setLayout(new GridLayout(1, false));

			Label lblNewLabel = new Label(composite_2, SWT.NONE);
			lblNewLabel.setFont(SWTResourceManager.getFont("Tahoma", 8, SWT.BOLD));
			lblNewLabel.setText("X-axis planet position:");
			GridData gd_lblNewLabel = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
			gd_lblNewLabel.widthHint = 134;
			gd_lblNewLabel.heightHint = 17;
			lblNewLabel.setLayoutData(gd_lblNewLabel);

			text_0 = new Text(composite_2, SWT.BORDER);
			text_0.addListener(SWT.Verify, new ValidationTextField());
			text_0.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND));
			text_0.setText(Double.toString(SolarSystem.m_x));
			GridData gd_text = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
			gd_text.widthHint = 150;
			text_0.setLayoutData(gd_text);

			Label lblNewLabel_1 = new Label(composite_2, SWT.NONE);
			lblNewLabel_1.setFont(SWTResourceManager.getFont("Tahoma", 8, SWT.BOLD));
			lblNewLabel_1.setText("Y-axis planet position:");

			text_1 = new Text(composite_2, SWT.BORDER);
			text_1.addListener(SWT.Verify, new ValidationTextField());
			text_1.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND));
			text_1.setText(Double.toString(SolarSystem.m_y));
			GridData gd_text_1 = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
			gd_text_1.widthHint = 120;
			text_1.setLayoutData(gd_text_1);

			Label lblNewLabel_2 = new Label(composite_2, SWT.NONE);
			lblNewLabel_2.setFont(SWTResourceManager.getFont("Tahoma", 8, SWT.BOLD));
			lblNewLabel_2.setText("X-axis speed:");

			text_2 = new Text(composite_2, SWT.BORDER);
			text_2.addListener(SWT.Verify, new ValidationTextField());
			text_2.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND));
			text_2.setText(Double.toString(SolarSystem.m_vx));
			text_2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

			Label lblNewLabel_3 = new Label(composite_2, SWT.NONE);
			lblNewLabel_3.setFont(SWTResourceManager.getFont("Tahoma", 8, SWT.BOLD));
			lblNewLabel_3.setText("Y-axis speed:");

			text_3 = new Text(composite_2, SWT.BORDER);
			text_3.addListener(SWT.Verify, new ValidationTextField());
			text_3.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND));
			text_3.setText(Double.toString(SolarSystem.m_vy));
			text_3.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

			Label lblNewLabel_5 = new Label(composite_2, SWT.NONE);
			lblNewLabel_5.setFont(SWTResourceManager.getFont("Tahoma", 8, SWT.BOLD));
			lblNewLabel_5.setText("Integration step:");

			text_4 = new Text(composite_2, SWT.BORDER);
			text_4.addListener(SWT.Verify, new ValidationTextField());
			text_4.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND));
			text_4.setText(Double.toString(SolarSystem.m_st));
			text_4.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
			new Label(composite_2, SWT.NONE);

			Button isDrawTrajectory = new Button(composite_2, SWT.CHECK);
			isDrawTrajectory.setSelection(SolarSystem.isDrawTrajectory);
			isDrawTrajectory.addMouseListener(new MouseAdapter()
			{
				@Override
				public void mouseDown(MouseEvent e)
				{
					SolarSystem.isDrawTrajectory = !SolarSystem.isDrawTrajectory;
				}
			});
			isDrawTrajectory.setText("Draw trajectory");

			Button isDrawSecondLaw = new Button(composite_2, SWT.CHECK);
			isDrawSecondLaw.setSelection(SolarSystem.isDrawSecondLaw);
			isDrawSecondLaw.addMouseListener(new MouseAdapter()
			{
				@Override
				public void mouseDown(MouseEvent e)
				{
					SolarSystem.isDrawSecondLaw = !SolarSystem.isDrawSecondLaw;
				}
			});
			isDrawSecondLaw.setText("Kepler's Second Law");
		}
		catch (LWJGLException e)
		{
			e.printStackTrace();
		}

		m_canvasListener = new Listener()
		{
			public void handleEvent(Event event)
			{
				switch (event.type)
				{
					case SWT.MouseWheel:
					{
						SolarSystem.zoom(0.05f * event.count);
						break;
					}
					case SWT.KeyDown:
					{
						if (event.keyCode == SWT.ARROW_UP)
						{
							m_key_up = true;
						}
						else if (event.keyCode == SWT.ARROW_DOWN)
						{
							m_key_down = true;
						}
						else if (event.keyCode == SWT.ARROW_RIGHT)
						{
							m_key_right = true;
						}
						else if (event.keyCode == SWT.ARROW_LEFT)
						{
							m_key_left = true;
						}
						break;
					}
					case SWT.KeyUp:
					{
						if (event.keyCode == SWT.ARROW_UP)
						{
							m_key_up = false;
						}
						else if (event.keyCode == SWT.ARROW_DOWN)
						{
							m_key_down = false;
						}
						else if (event.keyCode == SWT.ARROW_RIGHT)
						{
							m_key_right = false;
						}
						else if (event.keyCode == SWT.ARROW_LEFT)
						{
							m_key_left = false;
						}
						break;
					}
				}
			}
		};

		canvas.addListener(SWT.MouseWheel, m_canvasListener);
		canvas.addListener(SWT.KeyDown, m_canvasListener);
		canvas.addListener(SWT.KeyUp, m_canvasListener);

		SolarSystem.initGL();
		if (Config.k_SHOW_FPS)
		{
			getDelta();
			m_lastFPS = getTime();
		}

		display.asyncExec(new Runnable()
		{
			// Main loop
			public void run()
			{
				if (!canvas.isDisposed())
				{
					if (isActive)
					{
						canvas.setCurrent();
						try
						{
							GLContext.useContext(canvas);
						}
						catch (LWJGLException e)
						{
							e.printStackTrace();
						}

						handleEvents();
						SolarSystem.render(canvas.getSize().x, canvas.getSize().y);
						if (Config.k_SHOW_FPS)
							updateFPS();
						if (Config.k_LIMITATIONS_FPS)
							sync(Config.k_MAX_FPS);

						canvas.swapBuffers();
					}
					display.asyncExec(this);
				}
			}
		});

		shell.open();
		shell.layout();
		// Event loop.
		while (!shell.isDisposed())
		{
			if (!display.readAndDispatch())
			{
				display.sleep();
			}
		}
	}

	public void handleEvents()
	{
		if (m_key_up)
		{
			SolarSystem.m_camera.RotateX(-0.1f);
		}
		if (m_key_down)
		{
			SolarSystem.m_camera.RotateX(0.1f);
		}
		if (m_key_left)
		{
			SolarSystem.m_camera.RotateV(-0.1f);
		}
		if (m_key_right)
		{
			SolarSystem.m_camera.RotateV(0.1f);
		}
	}

	/**
	 * Create contents of the window.
	 */
	private void createContents(final Display display)
	{
		shell = new Shell();
		Listener listener = new Listener()
		{
			public void handleEvent(Event event)
			{
				int style = SWT.OK | SWT.CANCEL | SWT.APPLICATION_MODAL;
				MessageBox box = new MessageBox(shell, style);
				box.setMessage("Exit the application?");				
				event.doit = box.open() == SWT.OK;
			}
		};
		shell.addListener(SWT.Close, listener);
		shell.addHelpListener(new HelpListener()
		{
			public void helpRequested(HelpEvent arg0)
			{
				Help.show();
			}
		});
		shell.setImage(SWTResourceManager.getImage(KeplerApp.class, "/icon/world.ico"));
		shell.setSize(800, 600);
		shell.setText("Kepler problem");
		shell.setLayout(new GridLayout(2, false));

		Menu menu = new Menu(shell, SWT.BAR);
		shell.setMenuBar(menu);

		MenuItem mntmNewSubmenu = new MenuItem(menu, SWT.CASCADE);
		mntmNewSubmenu.setText("Help");

		Menu menu_1 = new Menu(mntmNewSubmenu);
		mntmNewSubmenu.setMenu(menu_1);

		MenuItem menuItemHelp = new MenuItem(menu_1, SWT.NONE);
		menuItemHelp.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				Help.show();
			}
		});
		menuItemHelp.setImage(SWTResourceManager.getImage(KeplerApp.class, "/icon/helpCenter.png"));
		menuItemHelp.setText("Help Contents");

		new MenuItem(menu_1, SWT.SEPARATOR);

		MenuItem menuItemAbout = new MenuItem(menu_1, SWT.NONE);
		menuItemAbout.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				About about = new About(shell, SWT.TITLE | SWT.CLOSE | SWT.APPLICATION_MODAL);
				about.open();
			}
		});
		menuItemAbout.setText("About");

		GLData data = new GLData();
		data.doubleBuffer = true;
		canvas = new GLCanvas(shell, SWT.NONE, data);
		canvas.setBackground(SWTResourceManager.getColor(SWT.COLOR_BLACK));
		GridData gd_canvas = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
		gd_canvas.widthHint = 393;
		gd_canvas.heightHint = 288;
		canvas.setLayoutData(gd_canvas);
	}

	// ========================FPS==========================
	/**
	 * Best sync method that works reliably.
	 * 
	 * @param fps
	 *            The desired frame rate, in frames per second
	 */
	private static void sync(int fps)
	{
		long timeNow;
		long gapTo;
		long savedTimeLate;

		gapTo = Sys.getTimerResolution() / fps + timeThen;
		timeNow = Sys.getTime();
		savedTimeLate = timeLate;

		try
		{
			while (gapTo > timeNow + savedTimeLate)
			{
				Thread.sleep(1);
				timeNow = Sys.getTime();
			}
		}
		catch (InterruptedException e)
		{
			Thread.currentThread().interrupt();
		}

		if (gapTo < timeNow)
			timeLate = timeNow - gapTo;
		else
			timeLate = 0;

		timeThen = timeNow;
	}

	/**
	 * Calculate the FPS and set it in the title bar
	 */
	private void updateFPS()
	{
		if (getTime() - m_lastFPS > 1000)
		{
			shell.setText("FPS: " + m_currentFPS);
			m_currentFPS = 0;
			m_lastFPS += 1000;
		}
		m_currentFPS++;
	}

	/**
	 * Calculate how many milliseconds have passed since last frame.
	 * 
	 * @return milliseconds passed since last frame
	 */
	private int getDelta()
	{
		long time = getTime();
		int delta = (int) (time - m_lastFrame);
		m_lastFrame = time;
		return delta;
	}

	/**
	 * Get the time in milliseconds
	 * 
	 * @return The system time in milliseconds
	 */
	private long getTime()
	{
		return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
}
